package com.nickstumpos.android.easyVolume.playRinger.streams;

import android.content.Context;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.Vibrator;

import com.nickstumpos.android.easyVolume.playRinger.IPlaySample;

public class NotificationPlaySample implements IPlaySample {

	private class VibratorThread extends Thread {

		@Override
		public void run() {
			while (NotificationPlaySample.this.continueVibrating) {
				if (NotificationPlaySample.this.shouldVibrate()) {
					NotificationPlaySample.this.vibrator
							.vibrate(NotificationPlaySample.VIBRATE_LENGTH);
				}
				SystemClock.sleep(NotificationPlaySample.VIBRATE_LENGTH
						+ NotificationPlaySample.PAUSE_LENGTH);
			}
		}
	}

	private class Worker implements Runnable {

		private final Object	lock	= new Object();
		private Looper				looper;

		Worker(String name) {
			Thread t = new Thread(null, this, name);
			t.start();
			synchronized (this.lock) {
				while (this.looper == null) {
					try {
						this.lock.wait();
					} catch (InterruptedException ex) {
					}
				}
			}
		}

		public Looper getLooper() {
			return this.looper;
		}

		public void quit() {
			this.looper.quit();
		}

		public void run() {
			synchronized (this.lock) {
				Looper.prepare();
				this.looper = Looper.myLooper();
				this.lock.notifyAll();
			}
			Looper.loop();
		}
	}

	private static final int	PLAY_RING_ONCE			= 1;
	private static final int	STOP_RING						= 3;

	private static final int	VIBRATE_LENGTH			= 1000;																								// ms

	private static final int	PAUSE_LENGTH				= 1000;																								// ms
	// Uri for the ringtone.
	Uri												customRingtoneUri		= RingtoneManager
																										.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
	Ringtone									notificationTone;
	Vibrator									vibrator;
	volatile boolean					continueVibrating;
	VibratorThread						vibratorThread;
	Context										context;
	private Worker						ringThread;
	private Handler						notificationHandler;
	private boolean						notificationPending;

	private long							firstRingEventTime	= -1;

	private long							firstRingStartTime	= -1;

	public NotificationPlaySample(Context main) {
		this.context = main;
		this.vibrator = (Vibrator) main.getSystemService(Context.VIBRATOR_SERVICE);
	}

	private boolean isRingtonePlaying() {
		synchronized (this) {
			return this.notificationTone != null
					&& this.notificationTone.isPlaying()
					|| this.notificationHandler != null
					&& this.notificationHandler
							.hasMessages(NotificationPlaySample.PLAY_RING_ONCE);
		}
	}

	public boolean isVibrating() {
		synchronized (this) {
			return this.vibratorThread != null;
		}
	}

	private void makeLooper() {
		if (this.ringThread == null) {
			this.ringThread = new Worker("ringer");
			this.notificationHandler = new Handler(this.ringThread.getLooper()) {

				@Override
				public void handleMessage(Message msg) {
					Ringtone r = null;
					switch (msg.what) {
						case PLAY_RING_ONCE:
							if (NotificationPlaySample.this.notificationTone == null
									&& !this.hasMessages(NotificationPlaySample.STOP_RING)) {
								r = RingtoneManager.getRingtone(
										NotificationPlaySample.this.context,
										NotificationPlaySample.this.customRingtoneUri);
								r.setStreamType(AudioManager.STREAM_NOTIFICATION);

								synchronized (NotificationPlaySample.this) {
									if (!this.hasMessages(NotificationPlaySample.STOP_RING)) {
										NotificationPlaySample.this.notificationTone = r;
									}
								}
							}
							r = NotificationPlaySample.this.notificationTone;
							while (r != null
									&& !this.hasMessages(NotificationPlaySample.STOP_RING)) {
								if (!r.isPlaying()) {
									r.play();
								}
								synchronized (NotificationPlaySample.this) {
									NotificationPlaySample.this.notificationPending = false;
									if (NotificationPlaySample.this.firstRingStartTime < 0) {
										NotificationPlaySample.this.firstRingStartTime = SystemClock
												.elapsedRealtime();
									}
								}
								try {
									Thread.sleep(NotificationPlaySample.PAUSE_LENGTH);
								} catch (Exception e) {
									e.printStackTrace();
								}
							}
							break;
						case STOP_RING:
							r = (Ringtone) msg.obj;
							if (r != null) {
								r.stop();
							}
							this.getLooper().quit();
							break;
					}
				}
			};
		}
	}

	public void setCustomRingtoneUri(Uri uri) {
		if (uri != null) {
			this.customRingtoneUri = uri;
		}
	}

	private boolean shouldVibrate() {
		AudioManager audioManager = (AudioManager) this.context
				.getSystemService(Context.AUDIO_SERVICE);
		return audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION);
	}

	@Override
	public void start() {

		synchronized (this) {

			if (this.vibratorThread == null) {
				this.continueVibrating = true;
				this.vibratorThread = new VibratorThread();
				this.vibratorThread.start();
			}

			if (!this.isRingtonePlaying() && !this.notificationPending) {
				this.makeLooper();
				this.notificationHandler.removeCallbacksAndMessages(null);
				this.notificationPending = true;
				if (this.firstRingEventTime < 0) {
					this.firstRingEventTime = SystemClock.elapsedRealtime();
					this.notificationHandler
							.sendEmptyMessage(NotificationPlaySample.PLAY_RING_ONCE);
				} else {
					if (this.firstRingStartTime > 0) {
						this.notificationHandler.sendEmptyMessageDelayed(
								NotificationPlaySample.PLAY_RING_ONCE, this.firstRingStartTime
										- this.firstRingEventTime);
					} else {
						this.firstRingEventTime = SystemClock.elapsedRealtime();
					}
				}
			}
		}

	}

	@Override
	public void stop() {
		synchronized (this) {
			if (this.notificationHandler != null) {
				this.notificationHandler.removeCallbacksAndMessages(null);
				Message msg = this.notificationHandler
						.obtainMessage(NotificationPlaySample.STOP_RING);
				msg.obj = this.notificationTone;
				this.notificationHandler.sendMessage(msg);
				this.ringThread = null;
				this.notificationHandler = null;
				this.notificationTone = null;
				this.firstRingEventTime = -1;
				this.firstRingStartTime = -1;
				this.notificationPending = false;
			}

			if (this.vibratorThread != null) {
				this.continueVibrating = false;
				this.vibratorThread = null;
			}
			this.vibrator.cancel();
		}

	}
}
